#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "diccionario.h"

#define NUM_FUNCTIONS 6

#define MAXNUM 1

//puntero a una funcion para modificar una palabra
typedef int (*pntFun)(dictioStruct *, char *);

int noMoreWords(dictioStruct * dictionary);
int returnUnchanged(dictioStruct * dictionary, char * dest);
int appendNum(dictioStruct * dictionary, char * dest);
int prependNum(dictioStruct * dictionary, char * dest);
int tryNum(dictioStruct * dictionary, char * dest);
int appendPrependNum(dictioStruct * dictionary, char * dest);
int duplicateWord(dictioStruct * dictionary, char * dest);

int
getKey(dictioStruct * dictionary, char * dest)
{
	// vector de funciones para aplicar transformaciones a las claves
	pntFun funcArr[NUM_FUNCTIONS] = {returnUnchanged, appendNum, prependNum,
			tryNum, appendPrependNum, duplicateWord};
	// en caseNum guardo el numero de la funcion que tengo que aplicar
	// a la clave
	static int caseNum = 0;
	int resp = 0;

	//mientras no tenga una posible respuesta, y aun me queden funciones para probar,
	//sigo intentando sacar la clave
	while (resp == 0 && caseNum < NUM_FUNCTIONS)
	{
		resp = funcArr[caseNum](dictionary, dest);
		if (resp == 0)
			caseNum++; //si me quede sin palabras, voy a probar con otra funcion
	}

	if (caseNum >= NUM_FUNCTIONS)
		caseNum = 0; //si me quede sin casos, lo reseteo para el proximo usuario

	return resp;
}

int
appendNumAux(dictioStruct * dictionary, char * dest, int maxLen, char * num)
{
	int flag, wordChosen = 0;

	while ( !wordChosen && (flag = noMoreWords(dictionary)) == 1 )
	{
		if (strlen(dictionary->words[dictionary->currentWord]) < maxLen)
		{
			strcpy(dest, dictionary->words[dictionary->currentWord]);
			strcat(dest, num);
			wordChosen = 1;
		}
		dictionary->currentWord++;
	}

	return flag && wordChosen;
}

int
prependNumAux(dictioStruct * dictionary, char * dest, int maxLen, char * num)
{
	int flag, wordChosen = 0;

	while ( !wordChosen && (flag = noMoreWords(dictionary)) == 1 )
	{
		if (strlen(dictionary->words[dictionary->currentWord]) < maxLen)
		{
			strcpy(dest, num);
			strcat(dest, dictionary->words[dictionary->currentWord]);
			wordChosen = 1;
		}
		dictionary->currentWord++;
	}

	return flag && wordChosen;
}

int
appendPrependNumAux(dictioStruct * dictionary, char * dest, int maxLen, char * num)
{
	int flag, wordChosen = 0;

	while ( !wordChosen && (flag = noMoreWords(dictionary)) == 1 )
	{
		if (strlen(dictionary->words[dictionary->currentWord]) < maxLen)
		{
			strcpy(dest, num);
			strcat(dest, dictionary->words[dictionary->currentWord]);
			strcat(dest, num);
			wordChosen = 1;
		}
		dictionary->currentWord++;
	}

	return flag && wordChosen;
}

//copia la palabra en dest sin modificaciones
int
returnUnchanged(dictioStruct * dictionary, char * dest)
{
	int flag;

	if ( (flag = noMoreWords(dictionary)) == 1)
	{
		strcpy(dest, dictionary->words[dictionary->currentWord]);
		dictionary->currentWord++;
	}
	return flag;
}

//agrega un numero al principio de la palabra
int
appendNum(dictioStruct * dictionary, char * dest)
{
	int flag = 0;
	static int num = 0; //en num guardo el numero a probar
	char numStr[3];
	int maxLen = dictionary->maxWordLen;

	while( flag == 0 && num <= MAXNUM )
	{
		sprintf(numStr, "%d", num);
		flag = appendNumAux(dictionary, dest, maxLen - strlen(numStr) + 1, numStr);
		if (flag == 0)
			num++; //si me quede sin palabras, pruebo con otro numero
	}

	if (num > MAXNUM)
		num = 0; //si me quede sin numeros, lo reseteo

	return flag;
}

//agrega un numero al final de la palabra
int
prependNum(dictioStruct * dictionary, char * dest)
{
	int flag = 0;
	static int num = 0; //en num guardo el numero a probar
	char numStr[3];
	int maxLen = dictionary->maxWordLen;

	while( flag == 0 && num <= MAXNUM )
	{
		sprintf(numStr, "%d", num);
		flag = prependNumAux(dictionary, dest, maxLen - strlen(numStr) + 1, numStr);
		if (flag == 0)
			num++; //si me quede sin palabras, pruebo con otro numero
	}

	if (num > MAXNUM)
		num = 0; //si me quede sin numeros, lo reseteo

	return flag;
}

//prueba con numeros del 1 al 9999
int
tryNum(dictioStruct * dictionary, char * dest)
{
	int flag = 1;
	static int num = 0;

	if ( num <= 9999)
	{
		sprintf(dest, "%d", num);
		num++;
	}
	else
	{
		flag = 0;
		num = 0;
	}
	return flag;
}

//agrega un numero al principio y final de una palabra
int
appendPrependNum(dictioStruct * dictionary, char * dest)
{
	int flag = 0;
	static int num = 0; //en num guardo el numero a probar
	char numStr[3];
	int maxLen = dictionary->maxWordLen;

	while( flag == 0 && num <= MAXNUM )
	{
		sprintf(numStr, "%d", num);
		flag = appendPrependNumAux(dictionary, dest, maxLen - strlen(numStr)*2 + 1, numStr);
		if (flag == 0)
			num++; //si me quede sin palabras, pruebo con otro numero
	}

	if (num > MAXNUM)
		num = 0; //si me quede sin numeros, lo reseteo

	return flag;
}

//prueba con duplicar una palabra (por ej, casa => casacasa)
int
duplicateWord(dictioStruct * dictionary, char * dest)
{
	int flag = 0, wordChosen = 0;
	int maxLen = dictionary->maxWordLen / 2;

	while ( !wordChosen && (flag = noMoreWords(dictionary)) == 1 )
	{
		if (strlen(dictionary->words[dictionary->currentWord]) < maxLen)
		{
			strcpy(dest, dictionary->words[dictionary->currentWord]);
			strcat(dest, dictionary->words[dictionary->currentWord]);
			wordChosen = 1;
		}
		dictionary->currentWord++;
	}

	return flag && wordChosen;
}

dictioStruct *
openDictionary(char * strDictioFile, int maxWordLen)
{
	FILE * dictioFile;
	char word[40];
	dictioStruct * dictionary;
	int size = 1000;
	int cont = 0;

	if ( (dictioFile = fopen(strDictioFile,"r")) == NULL)
		return NULL;
	if ( (dictionary = malloc(sizeof(dictioStruct))) == NULL)
		return NULL;
	if ( (dictionary->words = malloc(sizeof(char *)*size)) == NULL)
		return NULL;

	while ( !feof(dictioFile) )
	{
		if (cont >= size) //veo si realoco mas espacio para el diccionario
		{
			char ** aux;
			size += 1000;
			if ( (aux = realloc(dictionary->words, sizeof(char*) * size)) == NULL)
			{
				fclose(dictioFile);
				free(dictionary->words);
				free(dictionary);
				return NULL;
			}
			dictionary->words = aux;
		}

		int c, j = 0;
		while ( (c = getc(dictioFile) ) != EOF && c != '\n')
		{
			word[j++] = tolower(c);
		}
		word[j] = '\0';

		if ( j <= maxWordLen)
		{
			if ( (dictionary->words[cont] = malloc(sizeof(char)*(strlen(word)+1))) == NULL)
			{
				fclose(dictioFile);
				free(dictionary->words);
				free(dictionary);
				return NULL;
			}
			strcpy(dictionary->words[cont], word);
			cont++;
		}
	}

	fclose(dictioFile);
	dictionary->wordCount = cont;
	dictionary->currentWord = 0;
	dictionary->wordsSize = size;
	dictionary->maxWordLen = maxWordLen;

	return dictionary;
}

int
addWord(dictioStruct * dictionary, char * word)
{
	if (dictionary->wordCount >= dictionary->wordsSize)
	{
		char ** aux;
		if ( (aux = realloc(dictionary->words, sizeof(char*) * 100)) == NULL)
		{
			freeDictionary(dictionary);
			return -1;
		}
		dictionary->words = aux;
	}

	if ( (dictionary->words[dictionary->wordCount] =
		malloc(sizeof(char)*(strlen(word)+1))) == NULL)
	{
		freeDictionary(dictionary);
		return -1;
	}
	strcpy(dictionary->words[dictionary->wordCount], word);
	dictionary->wordCount++;

	return 1;
}

void freeDictionary(dictioStruct * dictionary)
{
	int i;

	for (i = 0; i < dictionary->wordCount; i++)
	{
		free(dictionary->words[i]);
	}
	free(dictionary->words);
	free(dictionary);
}

//devuelve 0 si no hay mas palabras en el diccionario, y resetea el
//contador de palabras
int
noMoreWords(dictioStruct * dictionary)
{
	//no tengo mas claves para probar
	if (dictionary->currentWord >= dictionary->wordCount )
	{
		dictionary->currentWord = 0;
		return 0;
	}
	return 1;
}
